/*
 * CDCTL01A STM32 Example
 * Demonstrates CDCTL01A chip usage with STM32 Arduino core
 * 
 * Hardware connections for STM32 Blue Pill:
 * - CDCTL01A CS  -> PA4 (SPI1_NSS)
 * - CDCTL01A INT -> PA0 (EXTI0)
 * - CDCTL01A MOSI -> PA7 (SPI1_MOSI)
 * - CDCTL01A MISO -> PA6 (SPI1_MISO)
 * - CDCTL01A SCK  -> PA5 (SPI1_SCK)
 * - CDCTL01A VCC  -> 3.3V
 * - CDCTL01A GND  -> GND
 */

#include <SimpleCDCTL.h>

// Pin definitions for STM32
const uint8_t CD_CS_PIN = PA4;
const uint8_t CD_INT_PIN = PA0;

// Create CDCTL01A instance
SimpleCDCTL cdctl(CD_CS_PIN, CD_INT_PIN);

// Device configuration
const uint8_t DEVICE_MAC = 0x02;  // Unique address for this STM32 device
const uint32_t BAUD_RATE = 2000000; // 2Mbps for STM32

// Communication buffers
uint8_t rx_buffer[128];
uint8_t tx_data[] = "STM32 CDCTL01A Test";

// Timing variables
uint32_t last_send = 0;
const uint32_t SEND_INTERVAL = 500; // Send every 500ms

// Statistics
uint32_t tx_count = 0;
uint32_t rx_count = 0;
uint32_t error_count = 0;

// LED indicators
const uint8_t LED_BUILTIN = PC13; // STM32 Blue Pill onboard LED

void setup() {
    Serial.begin(115200);
    while (!Serial) {
        ; // Wait for serial port
    }
    
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, HIGH); // LED off (active low)
    
    Serial.println("=== STM32 CDCTL01A Example ===");
    Serial.print("Device MAC: 0x");
    Serial.println(DEVICE_MAC, HEX);
    Serial.print("Baud Rate: ");
    Serial.println(BAUD_RATE);
    Serial.print("CPU Frequency: ");
    Serial.println(SystemCoreClock);
    
    // Initialize SPI with STM32 settings
    SPI.setMOSI(PA7);
    SPI.setMISO(PA6);
    SPI.setSCLK(PA5);
    SPI.begin();
    
    // Use higher SPI speed for STM32
    SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
    
    // Initialize CDCTL01A
    Serial.println("Initializing CDCTL01A...");
    if (!cdctl.begin(DEVICE_MAC, BAUD_RATE)) {
        Serial.println("Failed to initialize CDCTL01A!");
        blinkError(2);
    }
    
    Serial.println("CDCTL01A initialized successfully!");
    
    // Display chip information
    displayChipInfo();
    
    // Setup interrupt
    attachInterrupt(digitalPinToInterrupt(CD_INT_PIN), handleInterrupt, FALLING);
    
    Serial.println("Setup complete. Starting communication...");
    
    // Test SPI communication
    testSPICommunication();
}

void loop() {
    // Blink LED to show activity
    static uint32_t led_timer = 0;
    if (millis() - led_timer > 100) {
        digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
        led_timer = millis();
    }
    
    // Check for serial commands
    if (Serial.available()) {
        char cmd = Serial.read();
        handleSerialCommand(cmd);
    }
    
    // Periodic transmission
    if (millis() - last_send >= SEND_INTERVAL) {
        sendPeriodicMessage();
        last_send = millis();
    }
    
    // Check for received data
    if (cdctl.available()) {
        receiveData();
        digitalWrite(LED_BUILTIN, LOW); // LED on when receiving
    }
    
    // Handle interrupt-based reception
    if (interrupt_flag) {
        handleInterruptData();
    }
}

// Global interrupt flag
volatile bool interrupt_flag = false;

void handleInterrupt() {
    interrupt_flag = true;
}

void handleInterruptData() {
    noInterrupts();
    interrupt_flag = false;
    interrupts();
    
    // Process any pending data
    while (cdctl.available()) {
        receiveData();
    }
}

void testSPICommunication() {
    Serial.println("Testing SPI communication...");
    
    // Read status register
    uint8_t status = cdctl.getStatus();
    Serial.print("Status register: 0x");
    Serial.println(status, HEX);
    
    // Test register read/write
    uint8_t test_value = 0xAA;
    cdctl.writeRegister(0x05, test_value); // Write to filter mask
    uint8_t read_value = cdctl.readRegister(0x05);
    
    Serial.print("Register test - Write: 0x");
    Serial.print(test_value, HEX);
    Serial.print(" Read: 0x");
    Serial.println(read_value, HEX);
    
    if (test_value == read_value) {
        Serial.println("SPI communication test: PASSED");
    } else {
        Serial.println("SPI communication test: FAILED");
        error_count++;
    }
}

void displayChipInfo() {
    Serial.println("=== CDCTL01A Chip Information ===");
    
    for (uint8_t reg = 0x00; reg <= 0x06; reg++) {
        uint8_t value = cdctl.readRegister(reg);
        Serial.print("Reg 0x");
        if (reg < 0x10) Serial.print("0");
        Serial.print(reg, HEX);
        Serial.print(": 0x");
        if (value < 0x10) Serial.print("0");
        Serial.println(value, HEX);
    }
}

void sendPeriodicMessage() {
    // Create message with STM32 timestamp
    char message[64];
    snprintf(message, sizeof(message), "STM32@%lu", millis());
    
    if (cdctl.write((uint8_t*)message, strlen(message))) {
        tx_count++;
        Serial.print("[TX] ");
        Serial.print(message);
        Serial.print(" (");
        Serial.print(tx_count);
        Serial.println(")");
    } else {
        Serial.println("[TX] Failed");
        error_count++;
    }
}

void receiveData() {
    uint8_t length = cdctl.read(rx_buffer, sizeof(rx_buffer));
    if (length > 0) {
        rx_count++;
        
        // Print timestamp
        Serial.print("[RX@");
        Serial.print(millis());
        Serial.print("] ");
        
        // Print as hex and ASCII
        Serial.print("Hex: ");
        for (uint8_t i = 0; i < length; i++) {
            if (rx_buffer[i] < 0x10) Serial.print("0");
            Serial.print(rx_buffer[i], HEX);
            Serial.print(" ");
        }
        
        Serial.print("| ASCII: ");
        for (uint8_t i = 0; i < length; i++) {
            if (isprint(rx_buffer[i])) {
                Serial.print((char)rx_buffer[i]);
            } else {
                Serial.print(".");
            }
        }
        
        Serial.print(" (");
        Serial.print(rx_count);
        Serial.println(")");
    }
}

void handleSerialCommand(char cmd) {
    switch (cmd) {
        case 's':
        case 'S':
            Serial.println("Sending manual message...");
            sendManualMessage();
            break;
            
        case 'r':
        case 'R':
            Serial.println("Reading all pending data...");
            while (cdctl.available()) {
                receiveData();
            }
            break;
            
        case 'c':
        case 'C':
            showStatistics();
            break;
            
        case 'i':
        case 'I':
            displayChipInfo();
            break;
            
        case 't':
        case 'T':
            testSPICommunication();
            break;
            
        case 'm':
        case 'M':
            Serial.println("Testing maximum throughput...");
            testThroughput();
            break;
            
        default:
            Serial.println("Commands:");
            Serial.println("  s - Send manual message");
            Serial.println("  r - Read all pending");
            Serial.println("  c - Show statistics");
            Serial.println("  i - Show chip info");
            Serial.println("  t - Test SPI");
            Serial.println("  m - Test throughput");
            break;
    }
}

void sendManualMessage() {
    char message[64];
    snprintf(message, sizeof(message), "Manual@%lu", millis());
    
    if (cdctl.write((uint8_t*)message, strlen(message))) {
        Serial.println("Manual message sent");
    } else {
        Serial.println("Failed to send manual message");
    }
}

void showStatistics() {
    Serial.println("=== STM32 Statistics ===");
    Serial.print("TX Count: ");
    Serial.println(tx_count);
    Serial.print("RX Count: ");
    Serial.println(rx_count);
    Serial.print("Errors: ");
    Serial.println(error_count);
    Serial.print("Uptime: ");
    Serial.print(millis());
    Serial.println("ms");
    
    uint8_t status = cdctl.getStatus();
    Serial.print("Status: 0x");
    Serial.println(status, HEX);
    
    if (status & 0x01) Serial.println("  - RX pending");
    if (status & 0x02) Serial.println("  - TX pending");
    if (status & 0x04) Serial.println("  - RX overflow");
    if (status & 0x08) Serial.println("  - TX overflow");
}

void testThroughput() {
    Serial.println("Testing throughput...");
    
    uint32_t start_time = millis();
    uint32_t test_tx = 0;
    const uint32_t TEST_DURATION = 1000; // 1 second
    
    uint8_t test_data[64];
    memset(test_data, 0xAA, sizeof(test_data));
    
    while (millis() - start_time < TEST_DURATION) {
        if (cdctl.write(test_data, sizeof(test_data))) {
            test_tx++;
        }
    }
    
    uint32_t duration = millis() - start_time;
    uint32_t bytes_sent = test_tx * sizeof(test_data);
    float throughput = (float)bytes_sent * 1000 / duration;
    
    Serial.print("Throughput test completed:");
    Serial.print("  Messages: ");
    Serial.println(test_tx);
    Serial.print("  Bytes: ");
    Serial.println(bytes_sent);
    Serial.print("  Duration: ");
    Serial.print(duration);
    Serial.println("ms");
    Serial.print("  Throughput: ");
    Serial.print(throughput);
    Serial.println(" bytes/sec");
}

void blinkError(uint8_t count) {
    for (uint8_t i = 0; i < count; i++) {
        digitalWrite(LED_BUILTIN, LOW);
        delay(200);
        digitalWrite(LED_BUILTIN, HIGH);
        delay(200);
    }
    delay(1000);
}